package com.itaming.lycheeframework.support.utils;

import cn.hutool.core.net.Ipv4Util;
import lombok.experimental.UtilityClass;

import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * IP地址工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class IpAddressUtil {

    /**
     * IPv4地址正则表达式
     */
    private static final String IPV4_REGEX = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";

    /**
     * IPv4地址正则表达式模式
     */
    private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4_REGEX);

    /**
     * CIDR格式IPv4地址正则表达式
     */
    private static final String IPV4_CIDR_REGEX = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)/(\\d|[1-2]\\d|3[0-2])$";

    /**
     * CIDR格式IPv4地址正则表达式模式
     */
    private static final Pattern IPV4_CIDR_PATTERN = Pattern.compile(IPV4_CIDR_REGEX);

    /**
     * 判断是否是IPv4
     *
     * @param ip IP地址
     * @return boolean
     */
    public static boolean isIpv4(String ip) {
        return RegexUtil.matches(ip, IPV4_PATTERN);
    }

    /**
     * 判断是否为<code>CIDR</code>格式的IPv4
     *
     * @param ip IP地址
     * @return boolean
     */
    public static boolean isCidrIpv4(String ip) {
        return RegexUtil.matches(ip, IPV4_CIDR_PATTERN);
    }

    /**
     * 判断是否在IPv4范围内
     *
     * @param ipv4      IPv4地址
     * @param ipv4Range IPv4范围
     * @return boolean
     */
    public static boolean inIpv4Range(String ipv4, String ipv4Range) {
        return parseIpv4Range(ipv4Range).contains(ipv4);
    }

    /**
     * 解析IPv4范围
     *
     * @param ipv4Range IP地址范围
     * @return 包含的IPv4地址列表
     */
    public static List<String> parseIpv4Range(String ipv4Range) {
        if (StringUtil.isBlank(ipv4Range)) {
            return Collections.emptyList();
        }

        ipv4Range = ipv4Range.strip();

        if (isIpv4(ipv4Range)) {
            return Collections.singletonList(ipv4Range);
        }

        if (isCidrIpv4(ipv4Range)) {
            return parseCidrIpv4Range(ipv4Range);
        }

        if (ipv4Range.contains(StringPool.DASH)) {
            return parseDashedIpv4Range(ipv4Range);
        }

        if (ipv4Range.contains(StringPool.COMMA)) {
            return parseCommaDelimitedIpv4Range(ipv4Range);
        }

        return Collections.emptyList();
    }

    /**
     * 解析CIDR格式的IPv4范围
     * <p>
     * 如：
     * <blockquote>
     * <li>192.168.1.0/24</li>
     * <li>192.168.1.10/32</li>
     * </blockquote>
     *
     * @param ipv4Range IPv4地址范围
     * @return 包含的IP地址列表
     */
    private static List<String> parseCidrIpv4Range(String ipv4Range) {
        String[] ipPart = ipv4Range.split(StringPool.SLASH);
        return Ipv4Util.list(ipPart[0], Integer.parseInt(ipPart[1]), true);
    }

    /**
     * 解析横线格式的IPv4范围
     * <p>
     * 如：
     * <blockquote>
     * <li>192.168.1.1-10</li>
     * <li>192.168.1.1-192.168.1.10</li>
     * </blockquote>
     *
     * @param ipv4Range IPv4地址范围
     * @return 包含的IP地址列表
     */
    private static List<String> parseDashedIpv4Range(String ipv4Range) {
        String[] ipPart = ipv4Range.split(StringPool.DASH);
        String startIp = ipPart[0].trim();

        if (!isIpv4(startIp)) {
            return Collections.emptyList();
        }

        String endIp = ipPart[1].trim();
        if (!isIpv4(endIp)) {
            int end = NumberUtil.toInteger(endIp, -1);
            if (end < 0 || end > 255) {
                return Collections.emptyList();
            }

            String networkPrefix = startIp.substring(0, startIp.lastIndexOf(StringPool.DOT) + 1);

            endIp = networkPrefix + end;
        }

        return Ipv4Util.list(startIp, endIp);
    }

    /**
     * 解析逗号分割的IPv4范围
     * <p>
     * 如：
     * <blockquote>
     * <li>192.168.1.1,192.168.1.5,192.168.1.10</li>
     * </blockquote>
     *
     * @param ipv4Range IPv4地址范围
     * @return 包含的IP地址列表
     */
    private static List<String> parseCommaDelimitedIpv4Range(String ipv4Range) {
        return Stream.of(ipv4Range.split(StringPool.COMMA))
            .filter(StringUtil::isNotBlank)
            .map(String::strip)
            .filter(IpAddressUtil::isIpv4)
            .collect(Collectors.toList());
    }

}
